package com.test.demo1.common;

import com.test.demo1.bean.Items;
import com.test.demo1.service.ItemsService;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.*;
import org.redisson.config.Config;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;

@Component
@Slf4j
public class RedissonInventoryManager {
    private final RedissonClient redissonClient;
    private static final String SEGMENT_PREFIX = "inventory:segment:";
    private static final String TOTAL_KEY = "inventory:total";
    private static final String SEGMENT_LOCK_PREFIX = "lock:segment:";
    private static final int SEGMENT_COUNT = 10;
    private final ItemsService itemsService;
    private static final String STATUS_KEY = "inventory:status";
    private final ExecutorService asyncExecutor;

    // Lua脚本：原子性地扣减分段库存和总库存（整数版本）
    private static final String DEDUCT_SCRIPT =
            "local segmentKey = KEYS[1] " +
                    "local totalKey = KEYS[2] " +
                    "local statusKey = KEYS[3] " +
                    "local quantity = tonumber(ARGV[1]) " +
                    " " +
                    "local segmentQty = redis.call('get', segmentKey) " +
                    "if not segmentQty then " +
                    "   return 0 " +  // 分段不存在
                    "end " +
                    " " +
                    "segmentQty = tonumber(segmentQty) " +
                    "if not segmentQty then " +
                    "   return -1 " +  // 分段值不是有效数字
                    "end " +
                    " " +
                    "if segmentQty >= quantity then " +
                    "   local newSegmentQty = segmentQty - quantity " +
                    "   redis.call('set', segmentKey, newSegmentQty) " +
                    "   " +
                    "   local totalQty = redis.call('get', totalKey) " +
                    "   totalQty = tonumber(totalQty) or 0 " +
                    "   " +
                    "   local newTotalQty = totalQty - quantity " +
                    "   redis.call('set', totalKey, newTotalQty) " +
                    "   " +
                    "   if newTotalQty <= 0 then " +
                    "       redis.call('set', statusKey, '\"SOLD_OUT\"') " +
                    "   end " +
                    "   return 1 " +  // 成功
                    "else " +
                    "   return 0 " +  // 库存不足
                    "end";

    public RedissonInventoryManager(RedissonClient redissonClient, ItemsService itemsService) {
        this.redissonClient = redissonClient;
        this.itemsService = itemsService;
        this.asyncExecutor = Executors.newFixedThreadPool(4);

        // 预加载Lua脚本
        preloadLuaScript();
    }

    /**
     * 初始化库存分段
     * @param totalQty 总库存数量（整数）
     */
    public void initInventorySegments(int totalQty) {
        // 使用事务保证原子性
        RTransaction transaction = redissonClient.createTransaction(TransactionOptions.defaults());

        try {
            // 清理旧数据
            for (int i =  1; i <= SEGMENT_COUNT; i++) {
                transaction.getBucket(SEGMENT_PREFIX + i).delete();
            }
            transaction.getBucket(TOTAL_KEY).delete();
            transaction.getBucket(STATUS_KEY).delete();

            // 初始化数据
            List<Integer> segments = calculateSegments(totalQty);

            // 存储总库存
            transaction.getBucket(TOTAL_KEY).set(totalQty);

            // 存储分段库存
            for (int i = 0; i < segments.size(); i++) {
                String segmentKey = SEGMENT_PREFIX + (i + 1);
                transaction.getBucket(segmentKey).set(segments.get(i));
            }

            // 初始化状态
            transaction.getBucket(STATUS_KEY).set("AVAILABLE");

            transaction.commit();
            log.info("库存分段初始化完成，总库存: {}", totalQty);
            for (int i = 0; i < segments.size(); i++) {
                log.info("分段 {}: {}", (i + 1), segments.get(i));
            }

        } catch (Exception e) {
            transaction.rollback();
            log.error("库存初始化失败", e);
            throw new RuntimeException("库存初始化失败", e);
        }
    }

    /**
     * 计算库存分段
     * @param totalQty 总库存
     * @return 分段列表
     */
    private List<Integer> calculateSegments(int totalQty) {
        List<Integer> segments = new ArrayList<>();

        // 计算每个分段的基本数量
        int baseSegmentQty = totalQty / SEGMENT_COUNT;

        // 计算剩余数量（由于除法舍入可能产生的差额）
        int remaining = totalQty - (baseSegmentQty * SEGMENT_COUNT);

        // 创建前N-1个分段
        for (int i = 0; i < SEGMENT_COUNT - 1; i++) {
            segments.add(baseSegmentQty);
        }

        // 最后一个分段包含剩余的所有库存
        segments.add(baseSegmentQty + remaining);

        return segments;
    }

    /**
     * 扣减库存（高并发安全）- 使用Lua脚本保证原子性
     * @param quantity 需要扣减的数量
     * @return 是否扣减成功
     */
    public boolean deductInventory(int quantity) {
        // 随机选择起始分段，避免热点问题
        int startSegment = ThreadLocalRandom.current().nextInt(SEGMENT_COUNT) + 1;
        int currentSegment = startSegment;
        int attemptedSegments = 0;

        while (attemptedSegments < SEGMENT_COUNT) {
            String segmentKey = SEGMENT_PREFIX + currentSegment;
            String lockKey = SEGMENT_LOCK_PREFIX + currentSegment;

            // 获取分段锁
            RLock lock = redissonClient.getLock(lockKey);

            try {
                // 尝试获取锁，最多等待100ms，锁持有时间为5秒
                if (lock.tryLock(100, 5000, TimeUnit.MILLISECONDS)) {
                    try {
                        // 使用Lua脚本原子性执行扣减操作
                        Long result = redissonClient.getScript().eval(RScript.Mode.READ_WRITE,
                                DEDUCT_SCRIPT,
                                RScript.ReturnType.INTEGER,
                                Arrays.asList(segmentKey, TOTAL_KEY, STATUS_KEY),
                                quantity);

                        if (result != null) {
                            if (result == 1) {
                                log.info("成功扣减分段{}, 数量: {}", currentSegment, quantity);

                                // 检查是否需要更新数据库状态
                                String status = (String) redissonClient.getBucket(STATUS_KEY).get();
                                if ("SOLD_OUT".equals(status)) {
                                    asyncUpdateInventoryStatusInDB("SOLD_OUT");
                                }

                                return true;
                            } else if (result == -1) {
                                log.error("分段{}的值不是有效数字", currentSegment);
                                // 可以尝试修复数据或跳过此分段
                            }
                            // result == 0 表示库存不足，继续尝试下一个分段
                        }
                    } finally {
                        if (lock.isHeldByCurrentThread()) {
                            lock.unlock();
                        }
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("获取锁被中断", e);
                break;
            } catch (Exception e) {
                log.error("执行Lua脚本失败", e);
                // 可能是脚本加载问题，尝试重新加载脚本
                preloadLuaScript();
            }

            // 尝试下一个分段
            currentSegment = (currentSegment % SEGMENT_COUNT) + 1;
            attemptedSegments++;
        }

        log.warn("库存扣减失败，所有分段尝试完毕，数量: {}", quantity);
        return false;
    }

    /**
     * 异步更新数据库状态
     * @param status 状态（AVAILABLE/SOLD_OUT）
     */
    private void asyncUpdateInventoryStatusInDB(String status) {
        asyncExecutor.submit(() -> {
            String lockKey = "lock:db_update";
            RLock lock = redissonClient.getLock(lockKey);

            try {
                // 获取分布式锁，确保只有一个线程执行数据库更新
                if (lock.tryLock(100, 5000, TimeUnit.MILLISECONDS)) {
                    try {
                        // 再次检查库存状态，避免重复更新
                        String currentStatus = (String) redissonClient.getBucket(STATUS_KEY).get();
                        if ("SOLD_OUT".equals(currentStatus)) {
                            // 获取当前总库存
                            int currentTotal = getTotalInventory();

                            // 更新数据库
                            int rowsAffected = itemsService.updateById(Items.builder()
                                    .id(BigInteger.ONE)
                                    .qty(currentTotal)
                                    .build());

                            if (rowsAffected > 0) {
                                log.info("成功更新数据库库存状态为: SOLD_OUT, 库存: {}", currentTotal);
                            } else {
                                log.warn("更新数据库库存状态失败");
                            }
                        }
                    } finally {
                        if (lock.isHeldByCurrentThread()) {
                            lock.unlock();
                        }
                    }
                } else {
                    log.warn("获取数据库更新锁失败");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("数据库更新被中断", e);
            } catch (Exception e) {
                log.error("数据库更新异常", e);
            }
        });
    }

    /**
     * 预加载Lua脚本到Redis服务器
     */
    private void preloadLuaScript() {
        try {
            String sha1 = redissonClient.getScript().scriptLoad(DEDUCT_SCRIPT);
            log.info("Lua脚本预加载成功，SHA1: {}", sha1);
        } catch (Exception e) {
            log.error("Lua脚本预加载失败", e);
        }
    }

    /**
     * 获取当前总库存
     */
    public int getTotalInventory() {
        Integer total = (Integer) redissonClient.getBucket(TOTAL_KEY).get();
        return total != null ? total : 0;
    }

    /**
     * 增加库存（可用于补货等场景）
     * @param quantity 增加的数量
     */
    public void increaseInventory(int quantity) {
        String lockKey = "lock:total";
        RLock lock = redissonClient.getLock(lockKey);

        try {
            if (lock.tryLock(100, 5000, TimeUnit.MILLISECONDS)) {
                try {
                    // 获取当前总库存
                    int currentTotal = getTotalInventory();

                    // 增加总库存
                    int newTotal = currentTotal + quantity;
                    redissonClient.getBucket(TOTAL_KEY).set(newTotal);

                    // 如果之前是售罄状态，现在有库存了，更新状态
                    String status = (String) redissonClient.getBucket(STATUS_KEY).get();
                    if ("SOLD_OUT".equals(status) && newTotal > 0) {
                        redissonClient.getBucket(STATUS_KEY).set("AVAILABLE");
                        log.info("库存已补充，更新状态为可用，当前库存: {}", newTotal);
                    }

                    log.info("库存增加: {}, 新库存: {}", quantity, newTotal);
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("增加库存操作被中断", e);
        }
    }

    /**
     * 数据修复方法 - 如果发现数据不一致可以调用
     */
    public void repairInventoryData() {
        log.info("开始修复库存数据...");
        String lockKey = "lock:repair";
        RLock lock = redissonClient.getLock(lockKey);

        try {
            if (lock.tryLock(1000, 30000, TimeUnit.MILLISECONDS)) {
                try {
                    // 重新计算总库存
                    int total = 0;
                    for (int i = 1; i <= SEGMENT_COUNT; i++) {
                        String segmentKey = SEGMENT_PREFIX + i;
                        Integer segmentQty = (Integer) redissonClient.getBucket(segmentKey).get();

                        if (segmentQty != null) {
                            total += segmentQty;
                        } else {
                            // 分段不存在，初始化为0
                            redissonClient.getBucket(segmentKey).set(0);
                        }
                    }

                    // 更新总库存
                    redissonClient.getBucket(TOTAL_KEY).set(total);

                    // 更新状态
                    if (total <= 0) {
                        redissonClient.getBucket(STATUS_KEY).set("SOLD_OUT");
                    } else {
                        redissonClient.getBucket(STATUS_KEY).set("AVAILABLE");
                    }

                    log.info("库存数据修复完成，总库存: {}", total);
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("数据修复操作被中断", e);
        }
    }

    /**
     * 获取所有分段库存详情（用于监控和调试）
     */
    public Map<String, Integer> getSegmentDetails() {
        Map<String, Integer> segments = new HashMap<>();
        for (int i = 1; i <= SEGMENT_COUNT; i++) {
            String segmentKey = SEGMENT_PREFIX + i;
            Integer qty = (Integer) redissonClient.getBucket(segmentKey).get();
            segments.put("segment_" + i, qty != null ? qty : 0);
        }
        return segments;
    }

    /**
     * 关闭线程池
     */
    @PreDestroy
    public void shutdown() {
        asyncExecutor.shutdown();
        try {
            if (!asyncExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                asyncExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            asyncExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}
